#include "utils.h"

#include <stddef.h>
#include <ctype.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <dirent.h>
#include <sstream>
#include <cstring>
#include <cerrno>
using namespace std;


//-----------------------------------------------------------------------------------------
//                                  以下为辅助类和函数
//-----------------------------------------------------------------------------------------

class FdHolder
{
private:
    int fd;
public:
    FdHolder():fd(-1){}

    ~FdHolder() {
        if (fd != -1) {
            close(fd);
        }
    }

    int open(const char *path, int oflag, mode_t mode) {
        fd = ::open(path, oflag, mode);
        return fd;
    }

    int open(const char *path, int oflag) {
        fd = ::open(path, oflag);
        return fd;
    }
};



/*
把一行内容以第一个冒号为分隔符分为键值对，左边为键右边为值。
如果这一行没有冒号则返回false，成功拆分则返回true。
*/
static bool
splitLineToKV(const string &line, string *key, string *value)
{
    string::size_type i;
    string::size_type len = line.length();
    string::size_type pos = string::npos;

    for (i = 0; i < len; ++i) {
        if (line[i] == ':') {
            pos = i;
            break;
        }
    }

    if (pos == string::npos) {
        return false;
    }

    (*key) = trim(line.substr(0, pos));
    (*value) = trim(line.substr(pos + 1));
    return true;
}




//-----------------------------------------------------------------------------------------
//                              以下为实现头文件中定义的类和函数
//-----------------------------------------------------------------------------------------

extern string
readlink(const char *path)
{
    char buf[PATH_MAX];
    ssize_t len = readlink(path, buf, sizeof(buf));
    if (len == -1) {
        if (errno == EACCES) {
            return "";
        } else {
            ERROR(strerror(errno));
        }
    } else {
        buf[len] = '\0';
        string res = buf;
        return res;
    }
}

extern string
readlink(const string &path)
{
    return readlink(path.c_str());
}

extern string
trim(const string &s) {
    string::size_type len = s.length();
    string::size_type a,b;
    for (a = 0; a < len && isspace(s[a]); ++a) {
        // just scan , no loop body.
    }
    if (a == len) {
        return "";
    }
    for (b = len - 1; b < len && isspace(s[b]); --b) {
        // 这里不写b>=0而写成b<len是因为b是无符号数，比0减去1会导致回绕为最大值
        // just scan , no loop body.
    }
    return s.substr(a, b - a + 1);
}


void
Properties::loadFromText(const string &s)
{
    stringstream ss(s);
    string line;
    while (ss) {
        getline(ss, line);
        string k, v;
        if (splitLineToKV(line, &k, &v)) {
            names.push_back(k);
            map.insert(pair<string, string>(k, v));
        }
    }

}

void
Properties::loadFromFile(const char *path)
{
    string content = getFileContent(path);
    loadFromText(content);
}

extern string
getFileContent(const char *path)
{
    string str;
    FdHolder fdHolder;
    int fd = fdHolder.open(path, O_RDONLY);
    if (fd == -1) {
        if (errno == ENOENT) {
            throw NoSuchFile();
        } else {
            stringstream ss;
            ss << errno << "," << strerror(errno) << "#" << path;
            ERROR(ss.str());
        }
    }

    ssize_t n;
    char buf[BUFSIZ];

    while ((n = read(fd, buf, sizeof(buf) - 1)) > 0) {
        buf[n] = '\0';
        str += buf;
    }

    if (n == -1) {
        ERROR(strerror(errno));
    }

    return str;
}

extern string
getFileContent(const string &path)
{
    return getFileContent(path.c_str());
}










